package edu.sdjzu.exam.service.examSession.impl;

import org.springframework.messaging.simp.SimpMessagingTemplate;
import cn.dev33.satoken.stp.StpUtil;
import edu.sdjzu.exam.domain.exam.Exam;
import edu.sdjzu.exam.domain.exam.ExamAssignment;
import edu.sdjzu.exam.domain.examSession.Answer;
import edu.sdjzu.exam.domain.examSession.EnvironmentLog;
import edu.sdjzu.exam.domain.examSession.ExamLog;
import edu.sdjzu.exam.domain.examSession.ExamSession;
import edu.sdjzu.exam.dto.cheat.EnvironmentLogDTO;
import edu.sdjzu.exam.dto.examSession.*;
import edu.sdjzu.exam.mapper.exam.ExamAssignmentMapper;
import edu.sdjzu.exam.mapper.exam.ExamMapper;
import edu.sdjzu.exam.mapper.examSession.AnswerMapper;
import edu.sdjzu.exam.mapper.examSession.EnvironmentLogMapper;
import edu.sdjzu.exam.mapper.examSession.ExamLogMapper;
import edu.sdjzu.exam.mapper.examSession.ExamSessionMapper;
import edu.sdjzu.exam.mapper.group.UserGroupMemberMapper;
import edu.sdjzu.exam.service.examSession.ExamSessionService;
import edu.sdjzu.exam.service.redis.DraftRedisService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class ExamSessionServiceImpl implements ExamSessionService {

    private final ExamSessionMapper examSessionMapper;
    private final ExamMapper examMapper;
    private final ExamAssignmentMapper examAssignmentMapper;
    private final UserGroupMemberMapper userGroupMemberMapper;
    private final AnswerMapper answerMapper;
    private final DraftRedisService draftRedisService;
    private final ExamLogMapper examLogMapper;
    private final EnvironmentLogMapper environmentLogMapper;
    private final SimpMessagingTemplate messagingTemplate; // 添加注入

    @Override
    @Transactional
    public Long startExam(StartExamDTO startDTO) {
        Long studentId = StpUtil.getLoginIdAsLong();
        ExamAssignment assignment = examAssignmentMapper.selectExamAssignmentById(startDTO.getAssignmentId());
        if (assignment == null) {
            throw new RuntimeException("考试安排不存在");
        }
        Exam exam = examMapper.selectExamById(assignment.getExamId());
        if (exam == null) {
            throw new RuntimeException("试卷不存在");
        }
        if (userGroupMemberMapper.selectUserGroupMember(assignment.getGroupId(), studentId) == null) {
            throw new RuntimeException("无权限参加此考试");
        }
        ExamSession existingSession = examSessionMapper.selectExamSessionByStudentAndExam(studentId, assignment.getExamId());
        if (existingSession != null && !"interrupted".equals(existingSession.getStatus())) {
            throw new RuntimeException("考试会话已存在");
        }

        ExamSession session = new ExamSession();
        session.setExamId(assignment.getExamId());
        session.setStudentId(studentId);
        session.setStartTime(LocalDateTime.now());
        session.setEndTime(assignment.getEndTime());
        session.setRemainingTime((int) ChronoUnit.SECONDS.between(session.getStartTime(), assignment.getEndTime()));
        session.setIpAddress(startDTO.getIpAddress());
        session.setDeviceInfo(startDTO.getDeviceInfo());
        session.setStatus("started");
        session.setVideoStreamUrl(startDTO.getVideoStreamUrl());
        examSessionMapper.insertExamSession(session);

        ExamLog log = new ExamLog();
        log.setSessionId(session.getSessionId());
        log.setStudentId(studentId);
        log.setAction("start_exam");
        log.setDetails("开始考试");
        log.setIpAddress(startDTO.getIpAddress());
        log.setDeviceInfo(startDTO.getDeviceInfo());
        log.setActionTime(LocalDateTime.now());
        examLogMapper.insertLog(log);

        return session.getSessionId();
    }

    @Override
    @Transactional
    public void saveDraft(SaveDraftDTO draftDTO) {
        Long studentId = StpUtil.getLoginIdAsLong();
        ExamSession session = examSessionMapper.selectExamSessionById(draftDTO.getSessionId());
        if (session == null || !session.getStudentId().equals(studentId)) {
            throw new RuntimeException("无效的考试会话");
        }
        if (!"started".equals(session.getStatus()) && !"interrupted".equals(session.getStatus())) {
            throw new RuntimeException("考试会话已结束");
        }

        // 直接保存草稿到数据库，不再使用Redis
        Answer answer = answerMapper.selectAnswerBySessionAndQuestion(draftDTO.getSessionId(), draftDTO.getQuestionId());
        if (answer == null) {
            // 创建新的答案记录
            answer = new Answer();
            answer.setSessionId(draftDTO.getSessionId());
            answer.setExamId(session.getExamId());
            answer.setStudentId(studentId);
            answer.setQuestionId(draftDTO.getQuestionId());
            answer.setAnswerContent(draftDTO.getAnswerContent());
            answer.setFilePath(draftDTO.getFilePath());
            answer.setIsDraft(true);
            answer.setAnswerTime(draftDTO.getAnswerTime());
            answerMapper.insertAnswer(answer);
        } else {
            // 更新现有答案记录
            answer.setAnswerContent(draftDTO.getAnswerContent());
            answer.setFilePath(draftDTO.getFilePath());
            answer.setAnswerTime(draftDTO.getAnswerTime());
            answer.setIsDraft(true);
            answerMapper.updateAnswer(answer);
        }
        
        ExamLog log = new ExamLog();
        log.setSessionId(draftDTO.getSessionId());
        log.setStudentId(studentId);
        log.setAction("save_draft");
        log.setDetails("保存试题 " + draftDTO.getQuestionId() + " 的草稿");
        log.setIpAddress(session.getIpAddress());
        log.setDeviceInfo(session.getDeviceInfo());
        log.setActionTime(LocalDateTime.now());
        examLogMapper.insertLog(log);
    }

    @Override
    @Transactional
    public void logEnvironment(EnvironmentLogDTO envLogDTO) {
        Long studentId = StpUtil.getLoginIdAsLong();
        ExamSession session = examSessionMapper.selectExamSessionById(envLogDTO.getSessionId());
        if (session == null || !session.getStudentId().equals(studentId)) {
            throw new RuntimeException("无效的考试会话");
        }

        EnvironmentLog log = new EnvironmentLog();
        log.setSessionId(envLogDTO.getSessionId());
        log.setStudentId(studentId);
        log.setScreenSharing(envLogDTO.getScreenSharing());
        log.setVirtualMachine(envLogDTO.getVirtualMachine());
        log.setMultipleMonitors(envLogDTO.getMultipleMonitors());
        log.setFullscreenMode(envLogDTO.getFullscreenMode());
        log.setNetworkSpeed(envLogDTO.getNetworkSpeed());
        log.setNoiseLevel(envLogDTO.getNoiseLevel());
        log.setFaceDetected(envLogDTO.getFaceDetected());
        log.setSuggestions(envLogDTO.getSuggestions());
        log.setLogTime(envLogDTO.getLogTime());
        environmentLogMapper.insertEnvironmentLog(log);

        // 检测异常，推送警告
        if (!Boolean.TRUE.equals(envLogDTO.getFullscreenMode()) || !Boolean.TRUE.equals(envLogDTO.getFaceDetected()) || Boolean.TRUE.equals(envLogDTO.getScreenSharing())) {
            ExamSessionDTO sessionDTO = getExamSession(envLogDTO.getSessionId());
            messagingTemplate.convertAndSend("/topic/exam/" + session.getExamId(), List.of(sessionDTO));
        }
    }

    @Override
    @Transactional
    public void submitExam(SubmitExamDTO submitDTO) {
        Long studentId = StpUtil.getLoginIdAsLong();
        ExamSession session = examSessionMapper.selectExamSessionById(submitDTO.getSessionId());
        if (session == null || !session.getStudentId().equals(studentId)) {
            throw new RuntimeException("无效的考试会话");
        }
        if ("submitted".equals(session.getStatus())) {
            throw new RuntimeException("考试已提交");
        }

        // 直接提交数据库中的草稿答案，不再从Redis获取
        answerMapper.submitDrafts(session.getExamId(), studentId, LocalDateTime.now());
        session.setStatus("submitted");
        session.setEndTime(LocalDateTime.now());
        session.setRemainingTime(0);
        examSessionMapper.updateExamSession(session);

        ExamLog log = new ExamLog();
        log.setSessionId(submitDTO.getSessionId());
        log.setStudentId(studentId);
        log.setAction("submit_exam");
        log.setDetails("提交考试");
        log.setIpAddress(session.getIpAddress());
        log.setDeviceInfo(session.getDeviceInfo());
        log.setActionTime(LocalDateTime.now());
        examLogMapper.insertLog(log);
    }

    @Override
    @Transactional
    public ExamSessionDTO resumeExam(ResumeExamDTO resumeDTO) {
        Long studentId = StpUtil.getLoginIdAsLong();
        ExamSession session = examSessionMapper.selectExamSessionById(resumeDTO.getSessionId());
        if (session == null || !session.getStudentId().equals(studentId)) {
            throw new RuntimeException("无效的考试会话");
        }
        if ("submitted".equals(session.getStatus())) {
            throw new RuntimeException("考试已提交，无法续考");
        }

        List<Answer> drafts = answerMapper.selectDraftsByExamAndStudent(session.getExamId(), studentId);
        for (Answer draft : drafts) {
            SaveDraftDTO draftDTO = draftRedisService.getDraftFromRedis(resumeDTO.getSessionId(), draft.getQuestionId());
            if (draftDTO != null) {
                draft.setAnswerContent(draftDTO.getAnswerContent());
                draft.setFilePath(draftDTO.getFilePath());
                draft.setAnswerTime(draftDTO.getAnswerTime());
                answerMapper.updateAnswer(draft);
            }
        }

        session.setIpAddress(resumeDTO.getIpAddress());
        session.setDeviceInfo(resumeDTO.getDeviceInfo());
        session.setStatus("started");
        session.setRemainingTime((int) ChronoUnit.SECONDS.between(LocalDateTime.now(), session.getEndTime()));
        examSessionMapper.updateExamSession(session);

        ExamLog log = new ExamLog();
        log.setSessionId(resumeDTO.getSessionId());
        log.setStudentId(studentId);
        log.setAction("resume_exam");
        log.setDetails("恢复考试");
        log.setIpAddress(resumeDTO.getIpAddress());
        log.setDeviceInfo(resumeDTO.getDeviceInfo());
        log.setActionTime(LocalDateTime.now());
        examLogMapper.insertLog(log);

        return convertToDTO(session);
    }

    @Override
    public ExamSessionDTO getExamSession(Long sessionId) {
        Long studentId = StpUtil.getLoginIdAsLong();
        ExamSession session = examSessionMapper.selectExamSessionById(sessionId);
        if (session == null || !session.getStudentId().equals(studentId)) {
            throw new RuntimeException("无效的考试会话");
        }
        return convertToDTO(session);
    }

    @Override
    public List<ExamSessionDTO> monitorExamSessions(Long examId) {
        List<ExamSession> sessions = examSessionMapper.selectExamSessionsByExamId(examId);
        return sessions.stream().map(this::convertToDTO).collect(Collectors.toList());
    }

    @Override
    public List<ExamSessionDTO> getStudentExamSessions(StudentExamRecordsDTO requestDTO) {
        Long studentId = StpUtil.getLoginIdAsLong();
        
        // 根据当前学生ID查询考试会话
        // 这里需要在ExamSessionMapper中添加相应的查询方法
        List<ExamSession> sessions = examSessionMapper.selectExamSessionsByStudentId(studentId);
        
        // 如果有状态筛选条件
        if (requestDTO.getStatus() != null) {
            sessions = sessions.stream()
                .filter(session -> requestDTO.getStatus().equals(session.getStatus()))
                .collect(Collectors.toList());
        }
        
        // 如果有考试ID筛选条件
        if (requestDTO.getExamId() != null) {
            sessions = sessions.stream()
                .filter(session -> requestDTO.getExamId().equals(session.getExamId()))
                .collect(Collectors.toList());
        }
        
        return sessions.stream().map(this::convertToDTO).collect(Collectors.toList());
    }
    
    @Override
    public ExamSessionDTO getExamSessionByExamAndStudent(Long examId, Long studentId) {
        ExamSession session = examSessionMapper.selectExamSessionByStudentAndExam(studentId, examId);
        if (session == null) {
            throw new RuntimeException("考试会话不存在");
        }
        return convertToDTO(session);
    }

    private ExamSessionDTO convertToDTO(ExamSession session) {
        ExamSessionDTO dto = new ExamSessionDTO();
        dto.setSessionId(session.getSessionId());
        dto.setExamId(session.getExamId());
        dto.setStudentId(session.getStudentId());
        dto.setStartTime(session.getStartTime());
        dto.setEndTime(session.getEndTime());
        dto.setRemainingTime(session.getRemainingTime());
        dto.setIpAddress(session.getIpAddress());
        dto.setDeviceInfo(session.getDeviceInfo());
        dto.setStatus(session.getStatus());
        dto.setVideoStreamUrl(session.getVideoStreamUrl());
        dto.setCreatedAt(session.getCreatedAt());
        dto.setUpdatedAt(session.getUpdatedAt());

        List<Answer> drafts = answerMapper.selectDraftsByExamAndStudent(session.getExamId(), session.getStudentId());
        List<ExamSessionDTO.AnswerDTO> answerDTOs = drafts.stream().map(answer -> {
            ExamSessionDTO.AnswerDTO answerDTO = new ExamSessionDTO.AnswerDTO();
            answerDTO.setAnswerId(answer.getAnswerId());
            answerDTO.setQuestionId(answer.getQuestionId());
            answerDTO.setAnswerContent(answer.getAnswerContent());
            answerDTO.setFilePath(answer.getFilePath());
            answerDTO.setIsDraft(answer.getIsDraft());
            answerDTO.setAnswerTime(answer.getAnswerTime());
            return answerDTO;
        }).collect(Collectors.toList());
        dto.setAnswers(answerDTOs);

        return dto;
    }
}
